package cn.lg.soar.common.algorithm;

import cn.lg.soar.common.util.data.DataUtil;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

/**
 * hash摘要密码工具
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class CryptoUtils {

    private final static String HMACSHA256 = "HmacSHA256";
    private final static String HMACSHA384 = "HmacSHA384";
    private final static String HMACSHA512 = "HmacSHA512";

    private static MessageDigest md5;

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * md5
     * @param data 数据
     * @return
     */
    public static byte[] md5(byte[] data) {
        if (md5 == null) {
            try {
                md5 = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                throw new AlgorithmException(e);
            }
        }
        return md5.digest(data);
    }

    /**
     * md5（十六进制字符串）
     * @param data
     * @return
     */
    public static String md5Hex(byte[] data) {
        return DataUtil.byteToHex(md5(data));
    }

    /**
     * md5（base64）
     * @param data
     * @return
     */
    public static String md5Str(byte[] data) {
        return DataUtil.byteToUrlBase64(md5(data));
    }

    /**
     * sm3
     * @param data 数据
     * @return
     */
    public static byte[] sm3(byte[] data) {
        SM3Digest digest = new SM3Digest();
        digest.update(data, 0, data.length);
        byte[] hash = new byte[ digest.getDigestSize() ];
        digest.doFinal(hash, 0);
        return hash;
    }

    /**
     * sm3（十六进制字符串）
     * @param data 数据
     * @return
     */
    public static String sm3Hex(byte[] data) {
        return DataUtil.byteToHex(sm3(data));
    }

    /**
     * sm3（base64）
     * @param data 数据
     * @return
     */
    public static String sm3Str(byte[] data) {
        return DataUtil.byteToUrlBase64(sm3(data));
    }

    /**
     * hmac256
     * @param secret 盐
     * @param data 数据
     * @return
     */
    public static byte[] hmac256(byte[] secret, byte[] data) {
        return hmac(HMACSHA256, secret, data);
    }

    /**
     * hmac256（base64）
     * @param secret 盐
     * @param data 数据
     * @return
     */
    public static String hmac256Str(byte[] secret, byte[] data) {
        return DataUtil.byteToUrlBase64(hmac256(secret, data));
    }

    /**
     * hmac384
     * @param secret 盐
     * @param data 数据
     * @return
     */
    public static byte[] hmac384(byte[] secret, byte[] data) {
        return hmac(HMACSHA384, secret, data);
    }

    /**
     * hmac384（base64）
     * @param secret 盐
     * @param data 数据
     * @return
     */
    public static String hmac384Str(byte[] secret, byte[] data) {
        return DataUtil.byteToUrlBase64(hmac384(secret, data));
    }

    /**
     * hmac512
     * @param secret 盐
     * @param data 数据
     * @return
     */
    public static byte[] hmac512(byte[] secret, byte[] data) {
        return hmac(HMACSHA512, secret, data);
    }

    /**
     * hmac512（base64）
     * @param secret 盐
     * @param data 数据
     * @return
     */
    public static String hmac512Str(byte[] secret, byte[] data) {
        return DataUtil.byteToUrlBase64(hmac512(secret, data));
    }

    /**
     * 获取 HMAC 加密器
     * @param secret 盐
     * @return
     */
    public static Encoder HMAC256E(byte[] secret) {
        return new Encoder(HMACSHA256, secret);
    }

    /**
     * 获取 HMAC 加密器
     * @param secret 盐
     * @return
     */
    public static Encoder HMAC384E(byte[] secret) {
        return new Encoder(HMACSHA384, secret);
    }

    /**
     * 获取 HMAC 加密器
     * @param secret 盐
     * @return
     */
    public static Encoder HMAC512E(byte[] secret) {
        return new Encoder(HMACSHA512, secret);
    }

    private static byte[] hmac(String type, byte[] secret, byte[] data) {
        try {
            Mac mac = Mac.getInstance(type);
            mac.init(new SecretKeySpec(secret, type));
            return mac.doFinal(data);
        } catch (InvalidKeyException | NoSuchAlgorithmException e) {
            throw new AlgorithmException(e);
        }
    }

    private CryptoUtils() {}

    /**
     * hmac加密器
     */
    public static class Encoder {

        private Mac mac;
        private String type;
        private byte[] secret;

        public Encoder(String type, byte[] secret) {
            this.type = type;
            this.secret = secret;
        }

        public byte[] sign(byte[] data) {
            if (this.mac == null) {
                try {
                    Mac mac = Mac.getInstance(type);
                    mac.init(new SecretKeySpec(secret, type));
                    this.mac = mac;
                } catch (InvalidKeyException | NoSuchAlgorithmException e) {
                    throw new AlgorithmException(e);
                }
            }
            synchronized (this) {
                return this.mac.doFinal(data);
            }
        }

    }

}
